
#include <cstdio>
#include <BPainter>
#include <BEvent>
#include "member_BDisplay.h"
#include "member_BWidget.h"

using namespace BWE;

member_BDisplay::member_BDisplay(BDisplay* display)
{
	boss = display;
	size.set(1920, 1080);
	bgColor.set(66, 66, 128, 255);

	dirty = true;
	fbo = 0;
	rbo_depth = 0;
	rbo_stencil = 0;

	drag = 0;
	host = 0;
	hoverWidget = 0;
	focusWidget = 0;
}
member_BDisplay::~member_BDisplay()
{

}

bool member_BDisplay::check() const
{
	GLenum state = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	switch (state)
	{
	case GL_FRAMEBUFFER_COMPLETE:
		return true;
	case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
		puts("FBO Initialization Failed : incomplete attachment");
		return false;
	case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
		puts("FBO Initialization Failed : missing attachment");
		return false;
	case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
		puts("FBO Initialization Failed : draw buffer is invalid");
		return false;
	case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
		puts("FBO Initialization Failed : read buffer is invalid");
		return false;
	case GL_FRAMEBUFFER_UNSUPPORTED:
		puts("FBO Initialization Failed : this is not supported");
		return false;
	}

	return (bool)glIsFramebuffer(fbo);
}

void member_BDisplay::fresh()
{
	BEvent event(Event_Fresh);
	freshWidget(widget, event);
}
void member_BDisplay::frame()
{
	if (refresh)
	{
		this->save();

		glClearColor(bgColor.r() / 255.0f, bgColor.g() / 255.0f, bgColor.b() / 255.0f, bgColor.a() / 255.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		glDisable(GL_LIGHTING);
		glDisable(GL_DEPTH_TEST);

		refreshWindow();
		//paintCursor();
		//paintDrag();
		//paintTip();

		//BPainter painter(boss);
		//painter.setPointSize(20);
		//painter.setPointSmooth(true);
		//painter.setColor(255, 0, 0);
		//painter.drawPoint(mouse_pos);
		//
		//BString text;
		//if (hoverWidget)
		//{
		//	text << hoverWidget->className();
		//}
		//text << "(" << BString(mouse_pos.x()) << "," << BString(mouse_pos.y()) << ")";
		//painter.setColor(0, 0, 0);
		//painter.drawText(mouse_pos + BPoint(12, 0), text);
		this->open();

		refresh = false;
	}
}
void member_BDisplay::updateWidget(BWidget* widget, const BEvent& event)
{
	if (widget && widget->able())
	{
		widget_member(widget)->procEvent(event);
		if (widget_member(widget)->refresh)
			refresh = true;
		for (int i = 0; i < widget->childCount(); i++)
		{
			BWidget* child = widget->child(i);
			updateWidget(child, event);
		}
	}
}
void member_BDisplay::freshWidget(BWidget* widget, const BEvent& event)
{
	if (widget == 0)
		return;
	if (widget_member(widget)->refresh)
	{
		widget_member(widget)->refresh = false;
		widget_member(widget)->procEvent(event);
		widget_member(widget)->dirty = true;
		refresh = true;
	}
	for (int i = 0; i < widget->childCount(); i++)
	{
		BWidget* child = widget->child(i);
		freshWidget(child, event);
	}
}

bool member_BDisplay::hover(BWidget* widget)
{
	if (hoverWidget != widget)
	{
		hoverWidget = widget;
		if (widget)
			widget->fresh();
	}
	return true;
}
bool member_BDisplay::focus(BWidget* widget)
{
	if (focusWidget != widget)
	{
		if (focusWidget)
		{
			widget_member(focusWidget)->focus = false;
			BFocusEvent focusEvent(Focus_Out);
			widget_member(focusWidget)->procEvent(focusEvent);
			focusWidget = 0;
		}
		if (widget && widget->able())
		{
			widget_member(widget)->focus = true;
			BFocusEvent focusEvent(Focus_In);
			widget_member(widget)->procEvent(focusEvent);
			focusWidget = widget;

			if (popupWidgets.contain(focusWidget) && popupWidgets.last() != focusWidget)
			{
				popupWidgets.remove(focusWidget);
				popupWidgets.append(focusWidget);
			}
			focusWidget->fresh();
		}
		return true;
	}
	return false;
}

BWidget* member_BDisplay::widgetUnderPoint(const BPoint& point)
{
	if (popupWidgets.size())
	{
		for (int i = popupWidgets.size() - 1; i >= 0; i--)
		{
			BWidget* popupWidget = popupWidgets[i];
			BWidget* widget = findPopupWidget(popupWidget, point, host->size());
			if (widget)
				return widget;
			if (popupWidget->modal())
				return 0;
		}
	}
	return findWidget(host, point, size);
}
BWidget* member_BDisplay::findWidget(BWidget* widget, const BPoint& point, const BRect& clip)
{
	BRect rect = widget->rect();
	if (host == widget)
		rect.setPos(0, 0);
	BRect clipRect = rect & clip;
	if (!clipRect.contain(point))
		return 0;
	currentRect = clipRect;
	BPoint localPoint = point - rect.pos();
	for (int i = 0; i < widget_member(widget)->children.size(); i++)
	{
		BWidget* child = widget_member(widget)->children[i];
		if (!child->visible())
			continue;
		if (child->floating())
		{
			BWidget* ret = findWidget(child, localPoint, widget->size());
			if (ret)
				return ret;
			continue;
		}
		const BRect& clientRect = widget->clientRect();
		const BRect& leftRect = widget->rect(Part_Left);
		const BRect& topRect = widget->rect(Part_Top);
		const BRect& rightRect = widget->rect(Part_Right);
		const BRect& bottomRect = widget->rect(Part_Bottom);
		BRect childRect = child->rect();
		if (clientRect.intersect(childRect))
		{
			BWidget* ret = findWidget(child, localPoint, clientRect);
			if (ret)
				return ret;
		}
		if (topRect.intersect(childRect))
		{
			BWidget* ret = findWidget(child, localPoint, topRect);
			if (ret)
				return ret;
		}
		if (leftRect.intersect(childRect))
		{
			BWidget* ret = findWidget(child, localPoint, leftRect);
			if (ret)
				return ret;
		}
		if (rightRect.intersect(childRect))
		{
			BWidget* ret = findWidget(child, localPoint, rightRect);
			if (ret)
				return ret;
		}
		if (bottomRect.intersect(childRect))
		{
			BWidget* ret = findWidget(child, localPoint, bottomRect);
			if (ret)
				return ret;
		}
	}
	return widget;
}
BWidget* member_BDisplay::findPopupWidget(BWidget* widget, const BPoint& point, const BRect& clip)
{
	BRect viewRect = widget->absoluteRect() & clip;
	if (!viewRect.contain(point))
		return 0;

	for (int i = 0; i < widget->childCount(); i++)
	{
		BWidget* child = widget->child(i);
		if (child->visible())
		{
			BWidget* ret = findPopupWidget(child, point, viewRect);
			if (ret)
				return ret;
		}
	}

	return widget;
}

void member_BDisplay::refreshWindow()
{
	refreshWidget(widget);

	int width = size.width();
	int height = size.height();
	glViewport(0, 0, width, height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, width, height, 0, -1, 1);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	glDisable(GL_CULL_FACE);

	BRect clip = widget->rect();

	paintWidget(widget, clip, clip.pos(), height);

	for (int i = 0; i < popupWidgets.size(); i++)
	{
		BWidget* widget = popupWidgets[i];
		paintPopupWidget(widget, clip, widget->absolutePos(), height);
	}

	glBindTexture(GL_TEXTURE_2D, 0);
}
void member_BDisplay::refreshWidget(BWidget* widget)
{
	if (!widget_member(widget)->visible)
		return;
	if (widget_member(widget)->dirty)
	{
		widget_member(widget)->frame();
	}

	BWidgetHolderArray& children = widget_member(widget)->children;
	for (int i = children.size() - 1; i >= 0; i--)
	{
		BWidget* child = children[i];
		refreshWidget(child);
	}
}
void member_BDisplay::paintWidget(BWidget* widget, const BRect& clip, const BPoint& offset, int frameHeight)
{
	if (!widget_member(widget)->visible || popupWidgets.contain(widget))
		return;
	BSize size = widget->size();
	BRect ab_rect(offset, size);
	BRect view = ab_rect & clip;
	if (view.invalid())
		return;

	glViewport(view.x(), frameHeight - view.bottom(), view.width(), view.height());

	BRect area(view.pos() - ab_rect.pos(), view.size());
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(area.left(), area.right(), area.bottom(), area.top(), -1, 1);

	if (widget_member(widget)->tbo)
	{
		glBindTexture(GL_TEXTURE_2D, widget_member(widget)->tbo);

		int width = widget_member(widget)->size.width();
		int height = widget_member(widget)->size.height();

		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 1.0f);		glVertex2i(0, 0);
		glTexCoord2f(0.0f, 0.0f);		glVertex2i(0, height);
		glTexCoord2f(1.0f, 0.0f);		glVertex2i(width, height);
		glTexCoord2f(1.0f, 1.0f);		glVertex2i(width, 0);
		glEnd();
	}

	BWidgetHolderArray& children = widget_member(widget)->children;
	for (int i = children.size() - 1; i >= 0; i--)
	{
		BWidget* child = children[i];
		paintWidget(child, view, offset + child->pos(), frameHeight);
	}
}
void member_BDisplay::paintPopupWidget(BWidget* widget, const BRect& clip, const BPoint& offset, int frameHeight)
{
	if (!widget_member(widget)->visible)
		return;
	BSize size = widget->size();
	BRect ab_rect(offset, size);
	BRect view = ab_rect & clip;
	if (view.invalid())
		return;
	glViewport(view.x(), frameHeight - view.bottom(), view.width(), view.height());

	BRect area(view.pos() - ab_rect.pos(), view.size());
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(area.left(), area.right(), area.bottom(), area.top(), -1, 1);

	if (widget_member(widget)->tbo)
	{
		glBindTexture(GL_TEXTURE_2D, widget_member(widget)->tbo);

		int width = widget_member(widget)->size.width();
		int height = widget_member(widget)->size.height();

		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 1.0f);		glVertex2i(0, 0);
		glTexCoord2f(0.0f, 0.0f);		glVertex2i(0, height);
		glTexCoord2f(1.0f, 0.0f);		glVertex2i(width, height);
		glTexCoord2f(1.0f, 1.0f);		glVertex2i(width, 0);
		glEnd();
	}
	BWidgetHolderArray& children = widget_member(widget)->children;
	for (int i = 0; i < children.size(); i++)
	{
		BWidget* child = children[i];
		paintWidget(child, view, offset + child->pos(), frameHeight);
	}
}
void member_BDisplay::paintCursor()
{
	if (!hoverWidget)
		return;
	BPoint point = cursorPos;

	BCursor* cursor = hoverWidget->cursor();
	if (cursor && cursor->type() == Cursor_Custom)
	{
		BPainter painter(boss);

		BPoint origin = cursor->point();
		BSize size = cursor->size();
		int x = point.x() - origin.x();
		int y = point.y() - origin.y();
		int w = size.width();
		int h = size.height();

		BImage* image = cursor->image();
		if (image)
		{
			painter.setColor(255, 255, 255);
			painter.drawPoint(point);
			painter.drawImage(x, y, w, h, image);
		}
	}
}
void member_BDisplay::paintDrag()
{
	if (!drag)
		return;
	BPoint point = cursorPos;

	BPainter painter(boss);

	BPoint origin;
	BSize size(100, 100);
	int x = point.x() - origin.x();
	int y = point.y() - origin.y();
	int w = size.width();
	int h = size.height();

	BImage* image = drag->image();
	if (image)
	{
		painter.setColor(255, 255, 255);
		painter.drawPoint(point);
		painter.drawImage(x, y, w, h, image);
	}
}
void member_BDisplay::paintTip()
{
	if (!hoverWidget || !showTooltip)
		return;

	const BFont* font = widget->font();
	if (!font)
		return;

	const BString& tooltip = hoverWidget->tips();
	if (tooltip.empty())
		return;

	BSize textSize = font->textSize(tooltip);
	tipsRect.setSize(textSize);

	BPainter painter(boss);

	painter.setColor(Color_Background);
	painter.fillRect(tipsRect);
	painter.setColor(Value_Border);
	painter.drawRect(tipsRect);
	painter.setColor(Color_Text);
	painter.drawText(tipsRect, tooltip);
}

void member_BDisplay::slotMousePress(BMouseEvent& mouseEvent)
{
	mouse_pressed = true;
	mouse_pressed_time = mouseEvent.time();
	mouse_pressed_pos = mouseEvent.absolutePos();

	BWidget* widget = widgetUnderPoint(mouse_pressed_pos);
	this->focus(widget);
	if (focusWidget)
	{
		mouseEvent.setPos(mouseEvent.absolutePos() - focusWidget->absolutePos());
		widget_member(focusWidget)->procEvent(mouseEvent);
	}
}
void member_BDisplay::slotMouseRelease(BMouseEvent& mouseEvent)
{
	mouse_pressed = false;

	if (focusWidget)
	{
		mouseEvent.setPos(mouseEvent.absolutePos() - focusWidget->absolutePos());
		widget_member(focusWidget)->procEvent(mouseEvent);
	}
	if (focusWidget && mouse_click_time != 0)
	{
		int distance = (mouseEvent.absolutePos() - mouse_click_pos).length();
		double interval = mouseEvent.time() - mouse_click_time;
		if (distance < 4 && interval < 1)
		{
			//printf("double clicked : interval = %lf\n", interval);
			mouseEvent.setType(Event_DoubleClick);
			mouseEvent.setPos(mouseEvent.absolutePos() - focusWidget->absolutePos());
			widget_member(focusWidget)->procEvent(mouseEvent);
			mouse_click_time = 0;
			return;
		}
	}
	if (focusWidget)
	{
		int distance = (mouseEvent.absolutePos() - mouse_pressed_pos).length();
		double interval = mouseEvent.time() - mouse_pressed_time;
		if (distance < 5 && interval < 1)
		{
			printf("clicked : interval = %lf\n", interval);
			mouseEvent.setType(Event_MouseClick);
			mouseEvent.setPos(mouseEvent.absolutePos() - focusWidget->absolutePos());
			widget_member(focusWidget)->procEvent(mouseEvent);
			mouse_click_time = mouseEvent.time();
			mouse_click_pos = mouseEvent.absolutePos();
		}
	}
}
void member_BDisplay::slotMouseMove(BMouseEvent& mouseEvent)
{
	if (focusWidget && mouse_pressed)//block switch hovering widget
	{
		mouseEvent.setPos(mouseEvent.absolutePos() - focusWidget->absolutePos());
		widget_member(focusWidget)->procEvent(mouseEvent);
		return;
	}

	BPoint absolutePos = mouseEvent.absolutePos();
	BWidget* widget = widgetUnderPoint(absolutePos);
	if (hoverWidget != widget)
	{
		mouse_click_time = 0;
		if (hoverWidget)
		{
			BMouseEvent leaveEvent = mouseEvent;
			leaveEvent.setPos(mouseEvent.absolutePos() - hoverWidget->absolutePos());
			leaveEvent.setType(Event_MouseLeave);
			widget_member(hoverWidget)->procEvent(leaveEvent);
		}
		if (widget && widget->able())
		{
			BMouseEvent enterEvent = mouseEvent;
			enterEvent.setPos(mouseEvent.absolutePos() - widget->absolutePos());
			enterEvent.setType(Event_MouseEnter);
			widget_member(widget)->procEvent(enterEvent);
		}
		hover(widget);
	}
	if (hoverWidget && hoverWidget->able())
	{
		mouseEvent.setPos(mouseEvent.absolutePos() - hoverWidget->absolutePos());
		widget_member(hoverWidget)->procEvent(mouseEvent);
	}
}
void member_BDisplay::slotMouseWheel(BMouseEvent& mouseEvent)
{
	if (focusWidget && focusWidget->able())
	{
		widget_member(focusWidget)->procEvent(mouseEvent);
	}
}
void member_BDisplay::slotDragMove(BDragEvent& dragEvent)
{
	drag = dragEvent.drag();

	BPoint absolutePos = dragEvent.absolutePos();
	if (!host->absoluteRect().contain(absolutePos))
		drag = 0;

	BWidget* widget = widgetUnderPoint(absolutePos);
	if (hoverWidget != widget)
	{
		widget->fresh();
		mouse_click_time = 0;
		if (hoverWidget)
		{
			dragEvent.setCursor(0);
			dragEvent.setType(Event_DragLeave);
			dragEvent.setPos(dragEvent.absolutePos() - hoverWidget->absolutePos());
			widget_member(hoverWidget)->procEvent(dragEvent);
		}
		if (widget && widget->able())
		{
			dragEvent.setCursor(0);
			dragEvent.setType(Event_DragEnter);
			dragEvent.setPos(dragEvent.absolutePos() - widget->absolutePos());
			widget_member(widget)->procEvent(dragEvent);
		}
		hover(widget);
	}
	if (hoverWidget && hoverWidget->able())
	{
		dragEvent.setType(Event_DragMove);
		dragEvent.setPos(dragEvent.absolutePos() - hoverWidget->absolutePos());
		widget_member(hoverWidget)->procEvent(dragEvent);
	}
}
void member_BDisplay::slotDragDrop(BDragEvent& dragEvent)
{
	widget_member(hoverWidget)->procEvent(dragEvent);
	drag = 0;
}
